/*                                    _
 *   ___ _ __ ___ _ __ ___   ___   __| |
 *  / __| '__/ __| '_ ` _ \ / _ \ / _` |
 *  \__ \ |  \__ \ | | | | | (_) | (_| |
 *  |___/_|  |___/_| |_| |_|\___/ \__,_|                                  
 *
 *            _| _ |_     _ 
 *           (_|(/_|_)|_|(_|
 *                        _|
 *     __|_ _ _|_ _   |_  _ | _  _  _
 *    _\ | (_| | (/_  | |(/_||_)(/_| 
 *                          |               
 ******************************************
 ******************************************
 ******************************************
 *  _    |_ |. _   |`    _  __|_. _  _  _
 * |_)|_||_)||(_  ~|~|_|| |(_ | |(_)| |_\
 * |                                     
 *		// Normal debug print (l4d2_srs_debug_in & 1)
 *		Debug_Print(const String:format[], any:...)
 *
 *		// Spammy debug print (l4d2_srs_debug_in & 2)
 *		Debug_PrintSpam(const String:format[], any:...)
 *
 *		// Tries to search for a function signature, debug prints the success/failure
 *		Debug_SearchForFunction(Handle:conf, const String:functionName[])
 *
 ******************************************
 ******************************************
 ******************************************
 * . _ _  _ | _  _ _  _  _ _|_ _ _|_. _  _ 
 * || | ||_)|(/_| | |(/_| | | (_| | |(_)| |
 *       |                                 
 *		The idea is to allow configuration of debug input sources and 
 * 		output destinations via CVARs (l4d2_srs_debug_in and l4d2_srs_debug_out).
 * 
 *		Both of these cvars simply store multiple bitwise flags:
 *      	+ in: 1-normal, 2-spam
 *			+ out: 1-server, 2-log, 4-chat, 8-adminchat
 *
 * 		Debug_Print[Spam] when invoked check these flags accordingly and output only
 *		to the destinations that meet the criteria.
 *
 *		Ideally with this any user bug reports will always be accompanied
 *		with a debug log.
 */
 
static Handle:cvarDebugEnable 			= INVALID_HANDLE;
static Handle:cvarDebugIn 				= INVALID_HANDLE;
static Handle:cvarDebugOut 				= INVALID_HANDLE;

static const IN_NORMAL		= 1;
static const IN_SPAM		= 2;

static const OUT_SERVER		= 1;
static const OUT_LOG		= 2;
static const OUT_CHAT		= 4;
static const OUT_ADMINCHAT	= 8;
 
Debug_OnPluginStart()
{
	//oh the ugliness we must do because SourcePawn's preprocessor cannot stringize literals with #
	//and yet its DRY
	
#define MAKE_STRING_FROM_INT(%1,%2)											\
	decl String:%1[16];														\
	IntToString(.num = (%2), .str = %1, .maxlength = sizeof(%1))
// #define

	MAKE_STRING_FROM_INT(enable, SRS_DEBUG);
	MAKE_STRING_FROM_INT(in, (SRS_DEBUG | SRS_DEBUG_LOG)*IN_NORMAL + SRS_DEBUG_SPAM*IN_SPAM);
	MAKE_STRING_FROM_INT(out, SRS_DEBUG*OUT_SERVER + SRS_DEBUG_LOG*OUT_LOG + SRS_DEBUG*OUT_CHAT);
	
#undef MAKE_STRING_FROM_INT

	cvarDebugEnable = CreateConVar("srs_debug", enable, "Enable debug output in srsmod", SRS_CVAR_DEFAULT_FLAGS);
	cvarDebugIn = CreateConVar("srs_debug_in", in, "Sum of debug flags for debug sources (1-normal, 2-spam)", SRS_CVAR_DEFAULT_FLAGS);
	cvarDebugOut = CreateConVar("srs_debug_out", out, "Sum of debug flags for debug outputs (1-server, 2-log, 4-chat, 8-adminchat)", SRS_CVAR_DEFAULT_FLAGS);
}

//Harmless signature search
Debug_SearchForFunction(Handle:conf, const String:functionName[])
{
	StartPrepSDKCall(SDKCall_Static);
	if(PrepSDKCall_SetFromConf(conf, SDKConf_Signature, functionName))
	{
		Debug_Print("Function '%s' initialized.", functionName);
	} 
	else
	{
		Debug_Print("Function '%s' NOT FOUND.", functionName);
	}
	EndPrepSDKCall();
}

//Standard debug print
Debug_Print(const String:format[], any:...)
{
	decl String:buffer[192];
	VFormat(buffer, sizeof(buffer), format, .varpos = 2);
	
	FilteredPrint(IN_NORMAL, buffer);
}

//Debug print for messages that are repeated continuously
Debug_PrintSpam(const String:format[], any:...)
{
	decl String:buffer[192];
	VFormat(buffer, sizeof(buffer), format, .varpos = 2);
	
	FilteredPrint(IN_SPAM, buffer);
}

static FilteredPrint(requiredInFlag, const String:buffer[])
{
	new bool:enabled = GetConVarBool(cvarDebugEnable);
	new inFlags = GetConVarInt(cvarDebugIn);
	new outFlags = GetConVarInt(cvarDebugOut);
	
	if(!enabled || !(inFlags & requiredInFlag) || !outFlags)
	{
		return;
	}
	
	if(outFlags & OUT_SERVER)
	{
		PrintToConsole(ENTITY_WORLD, "[SRSDBG] %s", buffer);
	}
	if(outFlags & OUT_LOG)
	{
		LogMessage("%s", buffer);
	}
	if(outFlags & OUT_CHAT)
	{
		PrintToChatAll("[SRSDBG] %s", buffer);
	}
	else if(outFlags & OUT_ADMINCHAT)
	{
		FOR_EACH_CLIENT(i)
		{
			if(CheckCommandAccess(i, "sm_chat", ADMFLAG_CHAT))
			{
				PrintToChat(i, "[SRSDBG] %s", buffer);
			}
		}
	}
}
